%{
#include <stdlib.h>
#include <string.h>
#include "DB.h"
#include "DBFile.h"
//#define DEBUG

typedef char* string;
#define YYSTYPE string

void debug_where(const struct whereCond * cond, int cond_num);

struct col_def_list {
    col*    cols;
    int     num;
};

typedef char fixstr[256];

struct col_insert_list {
    fixstr* names;
    int     num;
};

struct value_insert_list {
    colValue *  values;
    int         num;
};

struct set_exp {
    fixstr      name;
    colValue    value;
};

struct set_list {
    struct set_exp *   exps;
    int                num;
};

struct variable {
    fixstr  table;
    fixstr  col;
};

struct value_entry {
    int         type; //0: Variable, 1: Int, 2: Char, 3: NULL
    union {
        fixstr          charValue; 
        int             intValue;
        struct variable variable;
    };
};

struct cond_list {
    struct whereCond *  conds;
    int                 num;
};

struct col_list {
    struct variable *   variable;
    int                 num;
};

struct table_list {
    fixstr *    tables;
    int         num;
};

%}

%token INTEGER

%token QUIT
%token DROP TABLE
%token DELETE FROM WHERE
%token AND OR NOT
%token SELECT 
%token INSERT INTO VALUES
%token UPDATE SET
%token CREATE CHAR INT VARCHAR NIL
%token NOT_EQUAL GREATER_EQUAL LESS_EQUAL
%token TYPE_INT TYPE_CHAR TYPE_VARCHAR IS
%token IDEN STRING

%%

    commands: 
                single_command commands
            |   single_command
            ;

    single_command: command
                {
                    printf("SQL> ");
                }

    command:    drop
            |   create
            |   insert
            |   update
            |   delete
            |   select
            |   quit
            ;

    quit:       QUIT
                {
                    DBFileCloseAll();
                    exit(0);
                }
            ;

    drop :      DROP TABLE IDEN ';'
                {
#ifdef DEBUG
                    printf("DROP TABLE %s\n", $3);
#endif
                    printf("%d\n", drop((char *) $3));
                    free((char *)$3);
                }
            ;

    create:     CREATE TABLE IDEN '(' column_def_list ')' ';'
                {
                    struct col_def_list * col_list = 
                        (struct col_def_list *) $5;
                    col * cols = calloc(col_list->num, sizeof(col));
                    int i;
                    for (i = 0; i < col_list->num; i++)
                        memcpy(&cols[col_list->num - 1 - i],
                            &col_list->cols[i], sizeof(col));
                    free(col_list->cols);
#ifdef DEBUG
                    printf("CREATE TABLE %s:\n", $3);
                    for (i = 0; i < col_list->num; i++)
                        printf("  %s %d %d\n", cols[i].name,
                            cols[i].size, cols[i].type);
#endif
                    printf("%d\n", 
                        Create((const char *)$3, cols, col_list->num));
                    free((char *) $3);
                    free(cols);
                    free(col_list);
                }
            ;

    column_def_list:
                column_def ',' column_def_list
                {
                    struct col_def_list * col_list = 
                        (struct col_def_list *) $3;
                    col_list->num++;
                    col_list->cols = (col *) realloc(col_list->cols, 
                        col_list->num * sizeof(col));
                    memcpy(&col_list->cols[col_list->num - 1], $1, 
                            sizeof(col));
                    free((col *) $1);
                    $$ = (char*) col_list;
                }
            |   column_def
                {
                    struct col_def_list * col_list = 
                        malloc(sizeof(struct col_def_list));
                    col_list->num = 1;
                    col_list->cols = calloc(col_list->num, sizeof(col));
                    memcpy(col_list->cols, $1, sizeof(col));
                    free((col *) $1);
                    $$ = (char *) col_list;
                }
            ;

    column_def: IDEN datatype length allow_null
                {
                    col * thiscol = malloc(sizeof(col));
                    strcpy(thiscol->name, (char*) $1);
                    free($1);
                    thiscol->type = (int) $2;
                    if (thiscol->type == DATA_INT)
                        thiscol->size = 4;
                    else
                        thiscol->size = (int) $3;
                    $$ = (char*) thiscol;
                }
            ;

    datatype:   TYPE_INT
                {
                    $$ = (char*) DATA_INT;
                }

            |   TYPE_CHAR
                {
                    $$ = (char*) DATA_CHAR;
                }

            |   TYPE_VARCHAR
                {
                    $$ = (char*) DATA_VCHAR;
                }
            ;

    length:     '(' INTEGER ')'
                {
                    $$ = $2;
                }
            |
                {
                    $$ = (char*) 0;
                }
            ;    

    allow_null: NIL
                {
                    $$ = (char*) 1;
                }
            |   NOT NIL
                {
                    $$ = (char*) 0;
                }
            ;

    insert:     INSERT INTO IDEN col_insert 
                VALUES '(' value_insert_list ')' ';'
                {
                    int i;
                    struct col_insert_list * col_list
                        = (struct col_insert_list *) $4;

                    struct value_insert_list * value_list
                        = (struct value_insert_list *) $7;

#ifdef DEBUG
                    printf("INSERT INTO %s\n", $3);
#endif
        // NULL if isChar = 2 (=.=)

                    if (col_list != NULL && 
                        col_list->num != value_list->num) {
                        //ERROR
                        goto insert_end;
                    }
                    
                    char ** colNames = NULL;
                    if (col_list != NULL) {
                        colNames = malloc(sizeof(char *) * col_list->num);
                        for (i = 0; i < col_list->num; i++)
                            colNames[col_list->num - 1 - i] = 
                                (char *)col_list->names[i];
                    }
#ifdef DEBUG
                    printf("    Column List:\n");
                    if (col_list == NULL) {
                        printf("        NULL\n");
                    } else {
                        for (i = 0; i < col_list->num; i++)
                            printf("        %s\n", colNames[i]);
                    }
#endif

                    colValue * valueArr = malloc(sizeof(colValue) 
                        * value_list->num);
                    for (i = 0; i < value_list->num; i++) {
                        memcpy(&valueArr[value_list->num - 1 - i],
                            &value_list->values[i], sizeof(colValue));
}
#ifdef DEBUG
                    printf("    Value List:\n");
                    for (i = 0; i < value_list->num; i++)
                        switch (valueArr[i].isChar) {
                            case 1:
                                printf("        %d\n", 
                                    valueArr[i].value.intValue);
                                break;
                            case 0:
                                printf("        '%s'\n",
                                    valueArr[i].value.charValue);
                                break;
                            case 2:
                                printf("        NULL\n");
                                break;
                            default:
                                break;
                        }
#endif
                    //Call
                    if (col_list == NULL) {
                        printf("%d\n", insertNormal($3, valueArr));
                    } else {
                        printf("%d\n", 
                            insertChaos($3, valueArr, colNames));
                    }
                    //End Call
                    free(valueArr);
                    free(colNames);
                    free((char *) $3);
insert_end:
                    if (col_list != NULL) {
                        free(col_list->names);
                        free(col_list);
                    }
                    free(value_list->values);
                    free(value_list);
                }
            ;

    col_insert: '(' col_insert_list ')'
                {
                    $$ = $2;
                }
            |
                {
                    $$ = NULL;
                }
            ;

    col_insert_list:
                IDEN ',' col_insert_list
                {
                    struct col_insert_list * col_list = 
                        (struct col_insert_list *) $3;
                    col_list->num ++;
                    col_list->names = (fixstr *) realloc(col_list->names,
                        col_list->num * sizeof(fixstr));
                    strcpy(col_list->names[col_list->num - 1], $1);
                    free((char *) $1);
                    $$ = (char *) col_list;
                }
            |   IDEN
                {
                    struct col_insert_list * col_list = 
                        malloc(sizeof(struct col_insert_list));
                    col_list->num = 1;
                    col_list->names = malloc(sizeof(fixstr));
                    strcpy(col_list->names[0], $1);
                    free((char *) $1);
                    $$ = (char *) col_list;
                }
            ;

    value_insert_list:
                const_var ',' value_insert_list
                {
                    struct value_insert_list * value_list
                        = (struct value_insert_list *) $3;
                    value_list->num ++;
                    value_list->values = (colValue *)
                        realloc(value_list->values,
                        value_list->num * sizeof(colValue));
                    memcpy(&value_list->values[value_list->num - 1], $1, 
                        sizeof(colValue));
                    free((colValue *) $1);
                    $$ = (char *) value_list;
                }
            |   const_var
                {
                    struct value_insert_list * value_list
                        = malloc(sizeof(struct value_insert_list));
                    value_list->num = 1;
                    value_list->values = calloc(1, sizeof(colValue));
                    memcpy(value_list->values, $1, sizeof(colValue));
                    free((colValue *) $1);
                    $$ = (char *) value_list;
                }
            ;

    const_var:  INTEGER
                {
                    colValue * value = malloc(sizeof(colValue));
                    value->isChar = 1;
                    value->value.intValue = (int) $1;
                    $$ = (char *) value;
                }
            |   STRING
                {
                    colValue * value = malloc(sizeof(colValue));
                    value->isChar = 0;
                    strcpy(value->value.charValue, $1);
                    free((char *) $1);
                    $$ = (char *) value;
                }
            |   NIL
                {
                    colValue * value = malloc(sizeof(colValue));
                    value->isChar = 2;
                    $$ = (char *) value;
                }
            ;

    update: UPDATE IDEN SET set_list where_stmt ';'
                {
                    struct set_list * set_list = (struct set_list *) $4;
                    struct cond_list * cond_list = (struct cond_list *) $5;

                    int set_num = set_list->num;
                    char** colName = malloc(sizeof(char *) * set_num);
                    colValue * newValue = malloc(sizeof(colValue) * set_num);
                    char ** valueStr = malloc(sizeof(char *) * set_num);
                    int i;
                    for (i = 0; i < set_num; i++) {
                        colName[i] = (char *) 
                            &set_list->exps[set_num - 1 - i].name;
                        memcpy(&newValue[i], 
                            &set_list->exps[set_num - 1 - i].value,
                            sizeof(colValue));
                        switch (newValue[i].isChar) {
                            case 1:
                                valueStr[i] = malloc(sizeof(fixstr));
                                sprintf(valueStr[i], "%d", 
                                    newValue[i].value.intValue);
                                break;
                            case 0:
                                valueStr[i] = malloc(sizeof(fixstr));
                                sprintf(valueStr[i], "%s", 
                                    newValue[i].value.charValue);
                                break;
                            case 2:
                                valueStr[i] = NULL;
                                break;
                            default:
                                break;
                        }
                    }
#ifdef DEBUG
                    printf("UPDATE %s\n", $2);
                    printf("    Set:\n");
                    for (i = 0; i < set_num; i++) {
                        printf("        %s = ", colName[i]);
                        switch (newValue[i].isChar) {
                            case 1:
                                printf("%d\n", newValue[i].value.intValue);
                                break;
                            case 0:
                                printf("'%s'\n", newValue[i].value.charValue);
                                break;
                            case 2:
                                printf("NULL\n");
                                break;
                            default:
                                break;
                        }
                    }
#endif
                    if (cond_list != NULL) {
                        int cond_num = cond_list->num;
                        struct whereCond * cond = 
                            malloc(sizeof(struct whereCond) * cond_num);
                        for (i = 0; i < cond_num; i++) {
                            memcpy(&cond[i], &cond_list->conds[cond_num - 1 - i],
                                sizeof(struct whereCond));
                        }

#ifdef DEBUG
                        debug_where(cond, cond_num);
#endif
                        //START Do Something with WHERE
                        char** tables = malloc(sizeof(char *));
                        tables[0] = $2;
                        whereRet * ret = NULL;
                        Where(tables, 1, cond, cond_num, &ret);
                        if (ret != NULL) {
                            printf("%d\n",
                                update($2, ret, colName, valueStr, set_num));
                            while (ret != NULL) {
                                whereRet * tmp = ret;
                                ret = ret->nextRet;
                                while (tmp->locations != NULL) {
                                    whereLoc * loc = tmp->locations;
                                    free(loc->tableName);
                                    tmp->locations = loc->nextLoc;
                                    free(loc);
                                }
                                free(tmp);
                            }
                        }
                        free(tables);
                        //END Do Something
                        for (i = 0; i < cond_num; i++) {
                            if (cond[i].lType == VALUE_TYPE_VAR) {
                                if (cond[i].lTableName != NULL)
                                    free(cond[i].lTableName);
                                free(cond[i].lColName);
                            } else if (cond[i].lType == VALUE_TYPE_STR) {
                                free(cond[i].lChar);
                            }

                            if (cond[i].rType == VALUE_TYPE_VAR) {
                                if (cond[i].rTableName != NULL)
                                    free(cond[i].rTableName);
                                free(cond[i].rColName);
                            } else if (cond[i].rType == VALUE_TYPE_STR) {
                                free(cond[i].rChar);
                            }
                        }
                        free(cond);
                        free(cond_list->conds);
                        free(cond_list);
                    } else {
#ifdef DEBUG
                        printf("    Where: NONE\n");
#endif
                        //START Do Something with WHERE
                        struct whereCond * cond = 
                            malloc(sizeof(struct whereCond));
                        cond[0].op = OP_EQ;
                        cond[0].lType = VALUE_TYPE_INT;
                        cond[0].lInt = 1;
                        cond[0].rType = VALUE_TYPE_INT;
                        cond[0].rInt = 1;
                        char** tables = malloc(sizeof(char *));
                        tables[0] = $2;
                        whereRet * ret = NULL;
                        Where(tables, 1, cond, 1, &ret);
                        if (ret != NULL) {
                            printf("%d\n",
                                update($2, ret, colName, valueStr, set_num));
                            while (ret != NULL) {
                                whereRet * tmp = ret;
                                ret = ret->nextRet;
                                while (tmp->locations != NULL) {
                                    whereLoc * loc = tmp->locations;
                                    free(loc->tableName);
                                    tmp->locations = loc->nextLoc;
                                    free(loc);
                                }
                                free(tmp);
                            }
                        }
                        free(tables);
                        free(cond);
                        //END Do Something
                    }

                    for (i = 0; i < set_num; i++) {
                        if (valueStr[i] != NULL)
                            free(valueStr[i]);
                    }
                    free(valueStr);
                    free(newValue);
                    free(colName);
                    free(set_list->exps);
                    free(set_list);
                }
            ;

    set_list:   set_exp ',' set_list
                {
                    struct set_list * list = (struct set_list *) $3;
                    list->num ++;
                    list->exps = realloc(list->exps, 
                        sizeof(struct set_exp) * list->num);
                    memcpy(&list->exps[list->num - 1], $1,
                        sizeof(struct set_exp));
                    free((struct set_exp *) $1);
                    $$ = (char *) list;
                }
            |   set_exp
                {
                    struct set_list *  list = 
                        malloc(sizeof(struct set_list));
                    list->num = 1;
                    list->exps = calloc(1, sizeof(struct set_exp));
                    memcpy(list->exps, $1, sizeof(struct set_exp));
                    free((struct set_exp *) $1);
                    $$ = (char *) list;
                }
            ;

    set_exp:    IDEN '=' const_var
                {
                    struct set_exp * exp = malloc(sizeof(struct set_exp));
                    strcpy(exp->name, $1);
                    memcpy(&exp->value, $3, sizeof(colValue));
                    free((char *) $1);
                    free((colValue *) $3);
                    $$ = (char *) exp;
                }
            ;

    where_stmt: WHERE condition_list
                {
                    $$ = $2;
                }
            |
                {
                    $$ = NULL;
                }
            ;

    condition_list:
                condition connect_operator condition_list
                {
                    struct cond_list * conds = (struct cond_list *) $3;
                    struct whereCond * cond = (struct whereCond *) $1;   

                    conds->conds[conds->num - 1].conOp = (int) $2;

                    conds->num++;
                    conds->conds = realloc(conds->conds, 
                        conds->num * sizeof(struct whereCond));
                    memcpy(&conds->conds[conds->num - 1], cond, 
                        sizeof(struct whereCond));
                    free(cond);
                    $$ = (char *) conds;
                }
            |   condition
                {
                    struct cond_list * conds = 
                        malloc(sizeof(struct cond_list));

                    struct whereCond * cond = (struct whereCond *) $1;
                    conds->num = 1;
                    conds->conds = malloc(sizeof(struct whereCond));
                    memcpy(conds->conds, cond, sizeof(struct whereCond));
                    free(cond);
                    $$ = (char *) conds;
                }
            ;

    connect_operator:
                AND
                {
                    $$ = (char *) CON_AND;
                }
            |   OR
                {
                    $$ = (char *) CON_OR;
                }
            ;

    condition:  value operator value
                {
                    struct whereCond * cond = 
                        malloc(sizeof(struct whereCond));

                    struct value_entry * value = (struct value_entry *) $1;

                    int len;

                    switch (value->type) {
                        case 0: 
                            cond->lType = VALUE_TYPE_VAR;
                            if (strlen(value->variable.table) == 0) {
                                cond->lTableName = NULL;
                            } else {
                                len = strlen(value->variable.table);
                                cond->lTableName = malloc(len + 1);
                                sprintf(cond->lTableName, "%s", 
                                    value->variable.table);
                            }
                            len = strlen(value->variable.col);
                            cond->lColName = malloc(len + 1);
                            sprintf(cond->lColName, "%s",
                                    value->variable.col);
                            break;
                        case 1:
                            cond->lType = VALUE_TYPE_INT;
                            cond->lInt = value->intValue;
                            break;
                        case 2:
                            cond->lType = VALUE_TYPE_STR;
                            len = strlen(value->charValue);
                            cond->lChar = malloc(len + 1);
                            sprintf(cond->lChar, "%s",
                                    value->charValue);
                            break;
                        case 3:
                            cond->lType = VALUE_TYPE_NULL;
                            break;
                        default:
                            break;
                    };
                    free(value);

                    cond->op = (int) $2;
                    
                    value = (struct value_entry *) $3;
                    switch (value->type) {
                        case 0: 
                            cond->rType = VALUE_TYPE_VAR;
                            if (strlen(value->variable.table) == 0) {
                                cond->rTableName = NULL;
                            } else {
                                len = strlen(value->variable.table);
                                cond->rTableName = malloc(len + 1);
                                sprintf(cond->rTableName, "%s", 
                                    value->variable.table);
                            }
                            len = strlen(value->variable.col);
                            cond->rColName = malloc(len + 1);
                            sprintf(cond->rColName, "%s",
                                    value->variable.col);
                            break;
                        case 1:
                            cond->rType = VALUE_TYPE_INT;
                            cond->rInt = value->intValue;
                            break;
                        case 2:
                            cond->rType = VALUE_TYPE_STR;
                            len = strlen(value->charValue);
                            cond->rChar = malloc(len + 1);
                            sprintf(cond->rChar, "%s",
                                    value->charValue);
                            break;
                        case 3:
                            cond->rType = VALUE_TYPE_NULL;
                            break;
                        default:
                            break;
                    };
                    free(value);

                    $$ = (char *) cond;
                }
            |   value IS NIL
                {
                    struct whereCond * cond = 
                        malloc(sizeof(struct whereCond));

                    struct value_entry * value = (struct value_entry *) $1;

                    int len;

                    switch (value->type) {
                        case 0: 
                            cond->lType = VALUE_TYPE_VAR;
                            if (strlen(value->variable.table) == 0) {
                                cond->lTableName = NULL;
                            } else {
                                len = strlen(value->variable.table);
                                cond->lTableName = malloc(len + 1);
                                sprintf(cond->lTableName, "%s", 
                                    value->variable.table);
                            }
                            len = strlen(value->variable.col);
                            cond->lColName = malloc(len + 1);
                            sprintf(cond->lColName, "%s",
                                    value->variable.col);
                            break;
                        case 1:
                            cond->lType = VALUE_TYPE_INT;
                            cond->lInt = value->intValue;
                            break;
                        case 2:
                            cond->lType = VALUE_TYPE_STR;
                            len = strlen(value->charValue);
                            cond->lChar = malloc(len + 1);
                            sprintf(cond->lChar, "%s",
                                    value->charValue);
                            break;
                        case 3:
                            cond->lType = VALUE_TYPE_NULL;
                            break;
                        default:
                            break;
                    };
                    free(value);
    
                    cond->op = OP_ISNIL;
                    
                    $$ = (char *) cond;
                }
            ;

    operator:   '='
                {
                    $$ = (char *) OP_EQ;
                }
            |   '>'
                {
                    $$ = (char *) OP_GT;
                }
            |   '<'
                {
                    $$ = (char *) OP_LT;
                }
            |   NOT_EQUAL
                {
                    $$ = (char *) OP_NE;
                }
            |   GREATER_EQUAL
                {
                    $$ = (char *) OP_GE;
                }
            |   LESS_EQUAL
                {
                    $$ = (char *) OP_LE;
                }
            ;

    value:      const_var
                {
                    struct value_entry * value = 
                        malloc(sizeof(struct value_entry));
                    colValue * const_var = (colValue *) $1;
                    if (const_var->isChar == 1) {
                        value->type = 1;
                        value->intValue = const_var->value.intValue;
                    } else if (const_var->isChar == 0) {
                        value->type = 2;
                        strcpy(value->charValue, const_var->value.charValue);
                    } else if (const_var->isChar == 2) {
                        value->type = 3;
                    }
                    free((colValue *) $1);
                    $$ = (char *) value;
                }
            |   variable
                {
                    struct value_entry * value = 
                        malloc(sizeof(struct value_entry));
                    struct variable * var = (struct variable *) $1;
                    value->type = 0;
                    memcpy(&value->variable, var, sizeof(struct variable));
                    free(var);
                    $$ = (char *) value;
                }
            ;

    variable:   IDEN
                {
                    struct variable * var = malloc(sizeof(struct variable));
                    strcpy(var->col, $1);
                    sprintf(var->table, "");
                    free((char *) $1);
                    $$ = (char *) var;
                }
            |   IDEN '.' IDEN
                {
                    struct variable * var = malloc(sizeof(struct variable));
                    strcpy(var->table, $1);
                    strcpy(var->col, $3);
                    free((char *) $1);
                    free((char *) $3);
                    $$ = (char *) var;
                }
            ;

    delete:     DELETE FROM IDEN where_stmt ';'
                {
                    
                    struct cond_list * cond_list = (struct cond_list *) $4;

                    int i;
#ifdef DEBUG
                    printf("DELETE FROM %s\n", $3);
#endif
                    if (cond_list != NULL) {
                        int cond_num = cond_list->num;
                        struct whereCond * cond = 
                            malloc(sizeof(struct whereCond) * cond_num);
                        for (i = 0; i < cond_num; i++) {
                            memcpy(&cond[i], &cond_list->conds[cond_num - 1 - i],
                                sizeof(struct whereCond));
                        }

#ifdef DEBUG
                        debug_where(cond, cond_num);
#endif
                        //START Do Something with WHERE
                        char** tables = malloc(sizeof(char *));
                        tables[0] = $3;
                        whereRet * ret = NULL;
                        Where(tables, 1, cond, cond_num, &ret);
#ifdef DEBUG
                        printf("WHERE FINISHED: %x\n", ret);
#endif
                        if (ret != NULL) {
                            printf("%d\n", Delete(ret));
                            while (ret != NULL) {
                                whereRet * tmp = ret;
                                ret = ret->nextRet;
                                while (tmp->locations != NULL) {
                                    whereLoc * loc = tmp->locations;
                                    free(loc->tableName);
                                    tmp->locations = loc->nextLoc;
                                    free(loc);
                                }
                                free(tmp);
                            }
                        }
                        free(tables);
                        //END Do Something
                        for (i = 0; i < cond_num; i++) {
                            if (cond[i].lType == VALUE_TYPE_VAR) {
                                if (cond[i].lTableName != NULL)
                                    free(cond[i].lTableName);
                                free(cond[i].lColName);
                            } else if (cond[i].lType == VALUE_TYPE_STR) {
                                free(cond[i].lChar);
                            }

                            if (cond[i].rType == VALUE_TYPE_VAR) {
                                if (cond[i].rTableName != NULL)
                                    free(cond[i].rTableName);
                                free(cond[i].rColName);
                            } else if (cond[i].rType == VALUE_TYPE_STR) {
                                free(cond[i].rChar);
                            }
                        }
                        free(cond);
                        free(cond_list->conds);
                        free(cond_list);
                    } else {
#ifdef DEBUG
                        printf("    Where: NONE\n");
#endif
                        //START Do Something with WHERE
                        struct whereCond * cond = 
                            malloc(sizeof(struct whereCond));
                        cond[0].op = OP_EQ;
                        cond[0].lType = VALUE_TYPE_INT;
                        cond[0].lInt = 1;
                        cond[0].rType = VALUE_TYPE_INT;
                        cond[0].rInt = 1;
                        char** tables = malloc(sizeof(char *));
                        tables[0] = $2;
                        whereRet * ret = NULL;
                        Where(tables, 1, cond, 1, &ret);
                        if (ret != NULL) {
                            printf("%d\n", Delete(ret));
                            while (ret != NULL) {
                                whereRet * tmp = ret;
                                ret = ret->nextRet;
                                while (tmp->locations != NULL) {
                                    whereLoc * loc = tmp->locations;
                                    free(loc->tableName);
                                    tmp->locations = loc->nextLoc;
                                    free(loc);
                                }
                                free(tmp);
                            }
                        }
                        free(tables);
                        free(cond);
                        //END Do Something
                    }
                }
            ;

    select:     SELECT col_sel FROM table_list where_stmt ';'
                {
                    struct col_list * col_list = (struct col_list *) $2;
                    struct table_list * table_list = (struct table_list *) $4;
                    struct cond_list * cond_list = (struct cond_list *) $5;

                    int i;

#ifdef DEBUG
                    printf("SELECT\n");
#endif

                    /* col_sel */
                    char ** selTable = NULL;
                    char ** selCol = NULL;
                    int selNum = 0;
                    if (col_list != NULL) {
                        selNum = col_list->num;
                        selTable = malloc(sizeof(char *) * selNum);
                        selCol = malloc(sizeof(char *) * selNum);
                        for (i = 0; i < selNum; i++) {
                            if (strlen(col_list->
                                    variable[selNum - 1 - i].table) == 0)
                                selTable[i] = NULL;
                            else
                                selTable[i] = (char *) 
                                    col_list->variable[selNum - 1 - i].table;
                            selCol[i] = (char *) 
                                col_list->variable[selNum - 1 - i].col;
                        }                        
                    }
#ifdef DEBUG
                    printf("    Columns:\n");
                    for (i = 0; i < selNum; i++) {
                        printf("        ");
                        if (selTable[i] != NULL)
                            printf("[%s].", selTable[i]);
                        printf("[%s]\n", selCol[i]);
                    }
#endif

                    /* table_list */
                    int fromNum = table_list->num;
                    char** fromTable = malloc(sizeof(char *) * fromNum);
                    for (i = 0; i < fromNum; i++) {
                        fromTable[i] = (char *) 
                            table_list->tables[fromNum - 1 - i];
                    }
#ifdef DEBUG
                    printf("    From:\n");
                    for (i = 0; i < fromNum; i++) {
                        printf("        %s\n", fromTable[i]);
                    }
#endif

                    /* where_stmt */
                    if (cond_list != NULL) {
                        int cond_num = cond_list->num;
                        struct whereCond * cond = 
                            malloc(sizeof(struct whereCond) * cond_num);
                        for (i = 0; i < cond_num; i++) {
                            memcpy(&cond[i], &cond_list->conds[cond_num - 1 - i],
                                sizeof(struct whereCond));
                        }

#ifdef DEBUG
                        debug_where(cond, cond_num);
#endif
                        //START Do Something with WHERE
                        whereRet * ret = NULL;
                        Where(fromTable, fromNum, cond, cond_num, &ret);
#ifdef DEBUG
                        printf("WHERE FINISHED: %x\n", ret);
#endif
                        if (ret != NULL) {
                            printf("%d\n", 
                                selectColumn(ret, selCol, selTable, selNum,
                                    fromTable, fromNum));
                            while (ret != NULL) {
                                whereRet * tmp = ret;
                                ret = ret->nextRet;
                                while (tmp->locations != NULL) {
                                    whereLoc * loc = tmp->locations;
                                    free(loc->tableName);
                                    tmp->locations = loc->nextLoc;
                                    free(loc);
                                }
                                free(tmp);
                            }
                        } else {
                            printf("NO RESULT\n");
                        }
                        //END Do Something
                        for (i = 0; i < cond_num; i++) {
                            if (cond[i].lType == VALUE_TYPE_VAR) {
                                if (cond[i].lTableName != NULL)
                                    free(cond[i].lTableName);
                                free(cond[i].lColName);
                            } else if (cond[i].lType == VALUE_TYPE_STR) {
                                free(cond[i].lChar);
                            }

                            if (cond[i].rType == VALUE_TYPE_VAR) {
                                if (cond[i].rTableName != NULL)
                                    free(cond[i].rTableName);
                                free(cond[i].rColName);
                            } else if (cond[i].rType == VALUE_TYPE_STR) {
                                free(cond[i].rChar);
                            }
                        }
                        free(cond);
                        free(cond_list->conds);
                        free(cond_list);
                    } else {
#ifdef DEBUG
                        printf("    Where: NONE\n");
#endif
                        //START Do Something with WHERE
                        printf("%d\n", 
                            selectColumn(NULL, selCol, selTable, selNum,
                                fromTable, fromNum));
                        //END Do Something
                    }

                    free(fromTable);
                    free(table_list->tables);
                    free(table_list);
                    if (col_list != NULL) {
                        free(selTable);
                        free(selCol);
                        free(col_list->variable);
                        free(col_list);
                    }
                }
            ;

    col_sel:    '*'
                {
                    $$ = NULL;
                }
            |   col_sel_list
                {
                    $$ = $1;
                }
            ;

    col_sel_list:
                variable ',' col_sel_list
                {
                    struct col_list * col_list = 
                        (struct col_list *) $3;                   
                    struct variable * variable = (struct variable *) $1;
                    col_list->num ++;
                    col_list->variable = realloc(col_list->variable,
                        sizeof(struct variable) * col_list->num);
                    memcpy(&col_list->variable[col_list->num - 1], variable,
                        sizeof(struct variable));
                    free(variable);
                    $$ = (char *) col_list;
                }
            |   variable
                {
                    struct col_list * col_list = 
                        malloc(sizeof(struct col_list));
                    col_list->variable = 
                        malloc(sizeof(struct variable));
                    struct variable * variable = (struct variable *) $1;
                    col_list->num = 1;
                    memcpy(col_list->variable, variable,
                        sizeof(struct variable));
                    free(variable);
                    $$ = (char *) col_list;
                }
            ;

    table_list: IDEN ',' table_list
                {
                    struct table_list * table_list = 
                        (struct table_list *) $3;
                    table_list->num ++;
                    table_list->tables = realloc(table_list->tables,
                        sizeof(fixstr) * table_list->num);
                    strcpy(table_list->tables[table_list->num - 1], 
                        (char *) $1);
                    free((char *) $1);
                    $$ = (char *) table_list;
                }
            |   IDEN
                {
                    struct table_list * table_list = 
                        malloc(sizeof(struct table_list));
                    table_list->num = 1;
                    table_list->tables = malloc(sizeof(fixstr));
                    strcpy(table_list->tables[0], (char *) $1);
                    free((char *) $1);
                    $$ = (char *) table_list;
                }
            ;

%%

int yyerror(char *msg)
{
    printf("Error encountered: %s \n", msg);
}

void debug_where(const struct whereCond * cond, int cond_num)
{
    int i;
    printf("    Where:\n");
    for (i = 0; i < cond_num; i++) {
        printf("        ");

        if (i != 0) {
            if (cond[i].conOp == CON_AND)
                printf("AND ");
            else
                printf("OR  ");
        } else {
            printf("    ");
        }

        if (cond[i].lType == VALUE_TYPE_NULL)
            printf("NULL");
        else if (cond[i].lType == VALUE_TYPE_VAR) {
            if (cond[i].lTableName != NULL)
                printf("[%s].[%s]", cond[i].lTableName, cond[i].lColName);
            else
                printf("[%s]", cond[i].lColName);
        }
        else if (cond[i].lType == VALUE_TYPE_INT)
            printf("%d", cond[i].lInt);
        else if (cond[i].lType == VALUE_TYPE_STR)
            printf("'%s'", cond[i].lChar);

        printf(" OP:%d ", cond[i].op);

        if (cond[i].rType == VALUE_TYPE_NULL)
            printf("NULL");
        else if (cond[i].rType == VALUE_TYPE_VAR) {
            if (cond[i].rTableName != NULL)
                printf("[%s].[%s]", cond[i].rTableName, cond[i].rColName);
            else
                printf("[%s]", cond[i].rColName);
        }
        else if (cond[i].rType == VALUE_TYPE_INT)
            printf("%d", cond[i].rInt);
        else if (cond[i].rType == VALUE_TYPE_STR)
            printf("'%s'", cond[i].rChar);

        printf("\n");
    }
}

